package xpTests;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import intervalTree.Interval;
import intervalTree.IntervalTree;

import org.junit.*;
import static org.junit.Assert.*;

public class TestIntervalTree {
	
	@Test public void defaultConstructor() {
		IntervalTree<Double> it = new IntervalTree<Double>();
		
		assertTrue(it.inSync());
		assertEquals(0, it.currentSize());
		
		assertEquals(0, it.get(0).size());
		assertEquals(0, it.get(0L,10L).size());
	}
	
	@Test public void alternateConstructor() {
		Interval<Double> int1 = new Interval<Double>(0L, 20L, 4.);
		Interval<Double> int2 = new Interval<Double>(5L, 25L, 2.);
		List<Interval<Double>> list = new ArrayList<Interval<Double>>();
		list.add(int1);
		list.add(int2);
		
		IntervalTree<Double> it = new IntervalTree<Double>(list);
		
		assertTrue(it.inSync());
		assertEquals(2, it.currentSize());
	}
	
	@Test public void buildRebuild() {
		IntervalTree<Double> it = new IntervalTree<Double>();
		assertTrue(it.inSync());
		assertEquals(0, it.currentSize());
		
		it.addInterval(0L, 10L, 5.);
		assertFalse(it.inSync());
		assertEquals(0, it.currentSize());
		
		it.build();
		assertTrue(it.inSync());
		assertEquals(1, it.currentSize());
	}
	
	@Test public void simpleStabQuery() {
		IntervalTree<Double> it = new IntervalTree<Double>();
		it.addInterval(0L,10L,5.);
		it.build();
	
		assertEquals(0,it.get(20L).size());
		assertEquals(1,it.get(5L).size());
	}
	
	@Test public void simpleIntervalQuery() {
		IntervalTree<Double> it = new IntervalTree<Double>();
		it.addInterval(0L,10L,5.);
		it.build();
		
		assertEquals(0,it.get(20L,30L).size());
		assertEquals(1,it.get(5L,15L).size());
	}
	
	@Test public void autoSync() {
		IntervalTree<Double> it = new IntervalTree<Double>();
		it.addInterval(0L,10L,5.);
		
		assertFalse(it.inSync());
		assertEquals(1,it.get(5L,15L).size());
	}
	
	@Test public void randomStabQueries() {
		List<Interval<Double>> list = randomList(100);
		IntervalTree<Double> it = new IntervalTree<Double>(list);
		
		for(int i = 0; i < 100; i++) {
			long stab = (long) (Math.random() * 100);
			
			//Calculate the correct result
			Set<Double> resultSet = new HashSet<Double>();
			for(Interval<Double> interval : list)
				if(interval.contains(stab))
					resultSet.add(interval.getData());
			
			//Perform the query
			Set<Double> querySet = new HashSet<Double>();
			List<Double> query = it.get(stab);
			for(Double doub : query)
				querySet.add(doub);
			
			assertTrue(resultSet.equals(querySet));
		}
	}
	
	@Test public void randomIntervalQueries() {
		List<Interval<Double>> list = randomList(100);
		IntervalTree<Double> it = new IntervalTree<Double>(list);
		
		for(int i = 0; i < 100; i++) {
			Interval<Double> inv = randomInterval();
			
			//Calculate the correct result
			Set<Double> resultSet = new HashSet<Double>();
			for(Interval<Double> interval : list)
				if(interval.intersects(inv)) 
					resultSet.add(interval.getData());
			
			//Perform the query
			Set<Double> querySet = new HashSet<Double>();
			List<Double> query = it.get(inv.getStart(), inv.getEnd());
			for(Double doub : query)
				querySet.add(doub);
			
			assertTrue(resultSet.equals(querySet));
		}
	}
	
	private List<Interval<Double>> randomList(int size) {
		List<Interval<Double>> result = new ArrayList<Interval<Double>>();
		for(int i = 0; i < size; i++)
			result.add(randomInterval());
		return result;
	}
	
	private Interval<Double> randomInterval() {
		long start = (long) (Math.random() * 100);
		long end = start + (long) (Math.random() * 10);
		Interval<Double> result = new Interval<Double>(start, end, Math.random());
		return result;
	}
}
